var assert     = require('assert'),
    utility    = require('system/utility'),
	LinkedList = require('collections/linkedList'),
	LinqTests  = require('./linq.Tests');

LinkedListTests = (function () {
	var LinkedListTests = function() {
		var validateLinkedList = function(list, expected) {
			var current = list.top;
			var previous;
			var next; 
			var equalFn
	
			assert.strictEqual(list.length, expected.length, 'incorrect length');
	
			for (var x = 0; x < expected.length; ++x) {
				if (utility.isPlainObject(expected[x]) || utility.isArray(expected[x])) {
					equalFn = assert.deepEqual;
				} else {
					equalFn = assert.strictEqual;
				}
		
				if (x === 0) {
					equalFn(list.top.value, expected[x]);
					assert.strictEqual(current.previous, undefined, 'the previous of the first item should be undefined');
				}
		
				if (x === expected.length - 1) {
					equalFn(list.bottom.value, expected[x]);
					assert.strictEqual(current.next, undefined, 'the next of the last item should be undefined');
				}
		
				equalFn(current.value, expected[x], 'unexpected item at index[' + x + ']');
		
				current = current.next;
			}
		}

		var dataSets = [
			{
				enumerable : new LinkedList(),
				result : []
			},
			{
				enumerable : [1].asEnumerable(),
				result : [1]
			},
			{
				enumerable : [1, 2, 3].asEnumerable(),
				result : [1, 2, 3],
				enumerable2 : [1, 2, 3].asEnumerable(),
				result2 : [1, 2, 3]
			}
		];

		var tests = {};

		utility.extend(
			true,
			tests,
			LinqTests(dataSets),
			{
				'#push(item, [item]...)' : {
					'pushing one item should add properly': function() {
						var list = new LinkedList();
					
						list.push('a');
					
						validateLinkedList(list, ['a']);
					},
					'pusing multiple items should add properly': function() {
						var list = new LinkedList();
					
						list.push('a', 'b', 'c');
					
						validateLinkedList(list, ['c', 'b', 'a']);
					
						list.push('d');
						list.push('e');
					
						validateLinkedList(list, ['e', 'd', 'c', 'b', 'a']);
					},
					'pushing multiple items of different types should push properly': function() {
						var list = new LinkedList();
						var fn   = function(item) { };
					
						list.push('a', 1, undefined, null);
					
						validateLinkedList(list, [null, undefined, 1, 'a']);
					
						list.push({ a: 123, b: { c: 'd' } });
						list.push(fn);
						list.push([1, 2, 3]);
					
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }, null, undefined, 1, 'a']);
					}
				},
				'#pop()' : {
					'poping one item should empty the list': function() {
						var list = new LinkedList();
					
						list.push(9);
						assert.strictEqual(list.pop().value, 9);
						validateLinkedList(list, []);
					},
					'poping items should come off in reverse order': function() {
						var list = new LinkedList();
					
						list.push('a', 'b', 'c', 'd');
					
						assert.strictEqual(list.pop().value, 'd');
						validateLinkedList(list, ['c', 'b', 'a']);
						assert.strictEqual(list.pop().value, 'c');
						validateLinkedList(list, ['b', 'a']);
						assert.strictEqual(list.pop().value, 'b');
						validateLinkedList(list, ['a']);
						assert.strictEqual(list.pop().value, 'a');
						validateLinkedList(list, []);
					},
					'poping an empty list should throw an exception': function() {
						var list = new LinkedList();
					
						assert.throws(list.pop, Error);
					},
					'poping a list of multiple types should come off in reverse order': function() {
						var list = new LinkedList();
						var fn   = function(item) { };
					
						list.push('a', 1, undefined, null, { a: 123, b: { c: 'd' } }, fn, [1, 2, 3]);
					
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }, null, undefined, 1, 'a']);
						assert.deepEqual(list.pop().value, [1, 2, 3]);
						validateLinkedList(list, [fn, { a: 123, b: { c: 'd' } }, null, undefined, 1, 'a']);
						assert.strictEqual(list.pop().value, fn);
						validateLinkedList(list, [{ a: 123, b: { c: 'd' } }, null, undefined, 1, 'a']);
						assert.deepEqual(list.pop().value, { a: 123, b: { c: 'd' } });
						validateLinkedList(list, [null, undefined, 1, 'a']);
						assert.strictEqual(list.pop().value, null);
						validateLinkedList(list, [undefined, 1, 'a']);
						assert.strictEqual(list.pop().value, undefined);
						validateLinkedList(list, [1, 'a']);
						assert.strictEqual(list.pop().value, 1);
						validateLinkedList(list, ['a']);
						assert.strictEqual(list.pop().value, 'a');
						validateLinkedList(list, []);
					}
				},
				'#chop()' : {
					'chopping one item should empty the list': function() {
						var list = new LinkedList();
					
						list.push(9);
						assert.strictEqual(list.chop().value, 9);
						validateLinkedList(list, []);
					},
					'chopping items should come off in FIFO order': function() {
						var list = new LinkedList();
					
						list.push('a', 'b', 'c', 'd');
					
						assert.strictEqual(list.chop().value, 'a');
						validateLinkedList(list, ['d', 'c', 'b']);
						assert.strictEqual(list.chop().value, 'b');
						validateLinkedList(list, ['d', 'c']);
						assert.strictEqual(list.chop().value, 'c');
						validateLinkedList(list, ['d']);
						assert.strictEqual(list.chop().value, 'd');
						validateLinkedList(list, []);
					},
					'chopping an empty list should throw an exception': function() {
						var list = new LinkedList();
					
						assert.throws(list.chop, Error);
					},
					'chopping a list of multiple types should come off in FIFO order': function() {
						var list = new LinkedList();
						var fn   = function(item) { };
					
						list.push('a', 1, undefined, null, { a: 123, b: { c: 'd' } }, fn, [1, 2, 3]);
					
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }, null, undefined, 1, 'a']);
						assert.strictEqual(list.chop().value, 'a');
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }, null, undefined, 1]);
						assert.strictEqual(list.chop().value, 1);
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }, null, undefined]);
						assert.strictEqual(list.chop().value, undefined);
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }, null]);
						assert.strictEqual(list.chop().value, null);
						validateLinkedList(list, [[1, 2, 3], fn, { a: 123, b: { c: 'd' } }]);
						assert.deepEqual(list.chop().value, { a: 123, b: { c: 'd' } });
						validateLinkedList(list, [[1, 2, 3], fn]);
						assert.strictEqual(list.chop().value, fn);
						validateLinkedList(list, [[1, 2, 3]]);
						assert.deepEqual(list.chop().value, [1, 2, 3]);
						validateLinkedList(list, []);
					}
				},
				'#clear()' : {
					'clearing an empty list should not error': function() {
						var list = new LinkedList();
					
						list.clear();
					
						validateLinkedList(list, []);
					},
					'clearing a list with different item types should empty the list': function() {
						var list = new LinkedList();
					
						list.push(1, 'a', undefined, null, { a: 123 });
						list.clear();
						validateLinkedList(list, []);
					}
				},
				'#top' : {
					'top should be read only': function() {
						var item = new LinkedList();
					
						item.push(1, 2, 3);
					
						assert.equal(item.top.value, 3);
						item.top = 4;
						assert.equal(item.top.value, 3);
					}
				},
				'#bottom' : {
					'bottom should be read only': function() {
						var item = new LinkedList();
					
						item.push(1, 2, 3);
					
						assert.equal(item.bottom.value, 1);
						item.bottom = 4;
						assert.equal(item.bottom.value, 1);
					}
				},
				'#length' : {
					'length should be read only': function() {
						var item = new LinkedList();
					
						item.push(1, 2, 3);
					
						assert.equal(item.length, 3);
						item.length = 4;
						assert.equal(item.length, 3);
					}
				}
			}
		);

		return tests;
	};

	return LinkedListTests;
})();

// Hook into commonJS module systems
if (typeof module !== 'undefined' && "exports" in module) {
  module.exports = LinkedListTests;
}